﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	MetaCtx.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Web.Caching;
using Microsoft.SharePoint;

namespace Smart.Common
{
	[Localizable(false)]
	public static class MetaCtx
	{
		/// <summary>
		/// Ensure static fields filled. DO NOT REMOVE!
		/// </summary>
		static MetaCtx()
		{
			// DO NOT REMOVE!
		}

		public static ReaderWriterLockSlim SyncLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

		public static TimeSpan DefaultMetadataTimeout = new TimeSpan(0, 1, 0, 0);
		public static CacheItemPriority DefaultMetadataPriority = CacheItemPriority.High;

#if CACHE_LAST_METASCOPE
		[ThreadStatic]
		private static string _lastScopeName;
		[ThreadStatic]
		private static MetaScope _lastMetaScope;
		[ThreadStatic]
		private static int _lastMetaScopeExpireCheckIdx;

		private const int LastMetaScopeExpireCheckMax = 10;
#endif

		public static MetaScope GetScope(string scopeName)
		{
			return
				GetScope(
					scopeName,
					scope =>
					{
						var ctx = SPServices.GetScope<IEntityScope>(scope);

						return ctx.Meta;
					});
		}

		public static string GetScopeKey(string scopeName)
		{
			return
				SPServices.Cache.GetKey(
					new object[] { Const.Cache.MetadataKey },
					true,
					false,
					false,
					false, null, new object[] { scopeName.GetHashCodeX() });
		}

		public static MetaScope GetScope(string scopeName, Func<string, MetaScope> scopeLoader)
		{
			SyncLock.EnterUpgradeableReadLock();

			MetaScope ms = null;

			try
			{
#if CACHE_LAST_METASCOPE
				if (_lastScopeName.EqIC(scopeName))
				{
					if (_lastMetaScopeExpireCheckIdx++ < LastMetaScopeExpireCheckMax)
					{
						ms = _lastMetaScope;
					}
				}

				if (ms == null)
				{
#endif
				var key = GetScopeKey(scopeName);

				ms = SPServices.Cache.Get(key, null) as MetaScope;

				if (ms == null && scopeLoader != null)
				{
					SyncLock.EnterWriteLock();

					try
					{
						ms = scopeLoader(scopeName);

						SPServices.Cache.Set(key, ms, DefaultMetadataTimeout, DefaultMetadataPriority);
					}
					finally
					{
						SyncLock.ExitWriteLock();
					}
				}

#if CACHE_LAST_METASCOPE
					if (ms != null)
					{
						_lastScopeName = scopeName;
						_lastMetaScope = ms;
						_lastMetaScopeExpireCheckIdx = 0;
					}
				}
#endif
			}
			finally
			{
				SyncLock.ExitUpgradeableReadLock();
			}

			return ms;
		}

		/// <summary>
		/// Current web
		/// </summary>
		public static MetaWebScope Web
		{
			get { return GetWeb(SPH.GetWeb()); }
		}

		public static string GetWebScopeName(SPWeb web)
		{
			return GetWebScopeName(web.ServerRelativeUrl);
		}

		public static string GetWebScopeName(string serverRelativeUrl)
		{
			return Const.Metadata.WebScopePrefix + (serverRelativeUrl.IsNullOrEmpty() ? "/" : serverRelativeUrl);
		}

		/// <summary>
		/// Root web
		/// </summary>
		public static MetaWebScope RootWeb
		{
			get { return GetWeb(SPH.GetRootWeb()); }
		}

		public static MetaWebScope GetWeb(SPWeb web)
		{
			return (MetaWebScope)GetScope(GetWebScopeName(web), webScopeName => LoadWebScope(web, null, null, true));
		}

		public static MetaWebScope LoadWebScope(SPWeb web, ICollection<string> includeLists, IDictionary<string, FacadeMappingInfo> mappingInfo, bool loadAll)
		{
			MetaWebScope mw = null;

			web.RunElevated(
				delegate
				{
					var cWeb = SPH.GetElevatedWeb();

					mw = new MetaWebScope(cWeb);

					mw.LoadLists(cWeb, includeLists, mappingInfo);

					if (loadAll)
					{
						mw.LoadContentTypes(cWeb);
						mw.LoadSubWebs(cWeb);
					}
				});

			return mw;
		}

		public static void SetWeb(SPWeb web, MetaWebScope scope)
		{
			var scopeName = GetWebScopeName(web);

			var key = GetScopeKey(scopeName);

			SPServices.Cache.Set(key, scope, DefaultMetadataTimeout, DefaultMetadataPriority);
		}

		/// <summary>
		/// Clear metadata cache
		/// </summary>
		public static void Clear()
		{
			SPServices.Cache.RemoveKeys(
				SPServices.Cache.GetKey(
					new object[] { Const.Cache.MetadataKey },
					true,
					false,
					false,
					false, null, null));
		}

		/// <summary>
		/// Return SPList from Web
		/// </summary>
		public static SPList List(SPWeb web, string titleOrUrl)
		{
			var meta = GetWeb(web);

			return meta.List(web, titleOrUrl);
		}

		/// <summary>
		/// Return SPList from Web
		/// </summary>
		public static SPList List(SPWeb web, Guid id)
		{
			SPList res = null;

			if (id != Guid.Empty)
			{
				try
				{
					res = web.Lists[id];
				}
				catch
				{
				}
			}

			if (res == null)
			{
				H.LT("MetaList:List.4 Exception: list '{0}' not found at '{1}'!", id, web.Title);
			}

			return res;
		}

		/// <summary>
		/// Return SPList from Web
		/// </summary>
		public static SPList List(string titleOrUrl)
		{
			return List(SPH.GetWeb(), titleOrUrl);
		}

		/// <summary>
		/// Return SPList from Web
		/// </summary>
		public static SPList List(Guid Id)
		{
			return List(SPH.GetWeb(), Id);
		}

		/// <summary>
		/// Return MetaList by Id
		/// </summary>
		public static MetaList MetaList(SPWeb web, Guid id)
		{
			var meta = GetWeb(web);

			return meta.List(id);
		}

		/// <summary>
		/// Return MetaList by TitleOrUrl
		/// </summary>
		public static MetaList MetaList(SPWeb web, string titleOrUrl)
		{
			var meta = GetWeb(web);

			return meta.List(titleOrUrl);
		}

		/// <summary>
		/// Return MetaList by Id
		/// </summary>
		public static MetaList MetaList(Guid id)
		{
			return MetaList(SPH.GetWeb(), id);
		}

		/// <summary>
		/// Return MetaList by TitleOrUrl
		/// </summary>
		public static MetaList MetaList(string titleOrUrl)
		{
			return MetaList(SPH.GetWeb(), titleOrUrl);
		}

		/// <summary>
		/// Return list Url by TitleOrUrl
		/// </summary>
		public static string ListUrl(SPWeb web, string titleOrUrl)
		{
			var meta = GetWeb(web);

			return meta.ListUrl(titleOrUrl);
		}

		/// <summary>
		/// Return list Url by Id
		/// </summary>
		public static string ListUrl(SPWeb web, Guid id)
		{
			var meta = GetWeb(web);

			return meta.ListUrl(id);
		}

		/// <summary>
		/// Return list Url by TitleOrUrl
		/// </summary>
		public static string ListUrl(string titleOrUrl)
		{
			return ListUrl(SPH.GetWeb(), titleOrUrl);
		}

		/// <summary>
		/// Return list Url by Id
		/// </summary>
		public static string ListUrl(Guid id)
		{
			return ListUrl(SPH.GetWeb(), id);
		}

		/// <summary>
		/// Return list field name by ListTitleOrUrl and FieldTitleOrName
		/// </summary>
		public static string FieldName(SPWeb web, string listTitleOrUrl, string fieldTitleOrName)
		{
			var meta = GetWeb(web);

			return meta.FieldName(listTitleOrUrl, fieldTitleOrName);
		}

		/// <summary>
		/// Return list field name by ListTitleOrUrl and FieldTitleOrName
		/// </summary>
		public static string FieldName(SPWeb web, string listTitleOrUrl, Guid id)
		{
			var meta = GetWeb(web);

			return meta.FieldName(listTitleOrUrl, id);
		}

		/// <summary>
		/// Return list field name by ListTitleOrUrl and FieldTitleOrName
		/// </summary>
		public static string FieldName(string listTitleOrUrl, string fieldTitleOrName)
		{
			return FieldName(SPH.GetWeb(), listTitleOrUrl, fieldTitleOrName);
		}

		/// <summary>
		/// Return list field name by ListTitleOrUrl and FieldTitleOrName
		/// </summary>
		public static string FieldName(string listTitleOrUrl, Guid id)
		{
			return FieldName(SPH.GetWeb(), listTitleOrUrl, id);
		}
	}
}